package ai;

import game.Game;

public class MinmaxImproved extends Minmax {

	protected final static int MINIMUM_VALUE = Integer.MIN_VALUE;
	protected final static int MAXIMUM_VALUE = Integer.MAX_VALUE;
	protected final static int LOSE_VALUE = -1000000;
	protected final static int WIN_VALUE = 1000000;
	
	//Set the maximum depth of recursion for the algorithm
	private final static int MAX_DEPTH = 6;
	
	protected int bestmove;
	
	public MinmaxImproved(Game game, byte side)
	{
		super(game, side);
	}
	
	protected int getZet()
	{
		//Copy a new playing field to the ai-array.
		copyPlayingField( this.game.getPlayingField() );
		
		//Set the counter to zero
		this.counter = 0;
		
		chooseMove( this.side, 0 );
		
		//System.out.println("MinmaxImproved counter: "+this.counter);
		
		return this.bestmove;
	}
	
	//Recursive function to determine the optimal move
	private int chooseMove(byte side, int depth)
	{
		byte opponent;
		int value;
		int bestmove = 0;
		int reply;
		
		//Increment the counter
		this.counter++;
		
		//Base case
		int basecase = evaluateField(this.side);
		if (depth >= MAX_DEPTH || basecase == LOSE_VALUE || basecase == WIN_VALUE || isDraw())
		{
			return basecase;
		}
		
		if (side == this.side)
		{
			opponent = this.opponent;
			value = MINIMUM_VALUE;
		}
		else
		{
			opponent = this.side;
			value = MAXIMUM_VALUE;
		}
		
		
		//Main loop
		for (int i = 0; i < Game.XSIZE; i++)
		{
			if ( ! this.columnIsFull(i) )
			{
				placeToken(i, side);
				reply = chooseMove(opponent, depth+1);
				removeToken(i);
				
				
				//Update the return value if we've found a better posibility
				if (side == this.side && reply > value ||
						side == this.opponent && reply < value)
				{
					value = reply;
					bestmove = i;
				}
				
			}
		}
		
		//Only update the bestmove at the highest level (depth 0)
		if (depth == 0)
		{
			this.bestmove = bestmove;
		}
		
		return value;
	}
	
	protected int evaluateField(byte playerSide)
	{
		return evaluateField(playerSide, this.aiPlayingField);
	}
	
	protected int evaluateField(byte playerSide, byte[][] playingField)
	{
		int tokensInARowCounter;
		int fieldSide;
		int x, y;
		int totalstrength = 0;
		int strength;
		boolean lose = false;
		
		//Check every location on the playingfield
		for(int i = 0; i < Game.XSIZE; i++)
		{
			for(int j = Game.YSIZE - 1; j >= 0; j--)
			{
				//If the locatie is empty go to the next column
				if (playingField[i][j] == Game.EMPTY)
				{
					//Skip to the next column
					break;
				}
				else
				{
					fieldSide = playingField[i][j];
					
					//Default strength to zero
					strength = 0;
					
					//Set the counter to 1 because we've already found one-on-row
					tokensInARowCounter = 1;
					
					//Check in front of the current location
					for (x = i+1; x < Game.XSIZE; x++)
					{
						if (fieldSide == playingField[x][j])
						{
							tokensInARowCounter++;
						}
						else
						{
							break;
						}
					}
					//Front - check
					switch (tokensInARowCounter)
					{
					case 1:
						strength++;
						break;
					case 2:
						strength += 4;
						break;
					case 3:
						strength += 32;
						break;
					case 4:
					default:
						if (fieldSide == playerSide)
							return WIN_VALUE;
						else
							lose = true;
					}


					//Reset the counter to 1
					tokensInARowCounter = 1;
					//Check to the upper right of the current location
					for (x = i+1, y = j-1; x < Game.XSIZE && y >= 0; x++, y--)
					{
						if (fieldSide == playingField[x][y])
						{
							tokensInARowCounter++;
						}
						else
						{
							break;
						}
					}
					//Diagonal - check
					switch (tokensInARowCounter)
					{
					case 1:
						strength++;
						break;
					case 2:
						strength += 4;
						break;
					case 3:
						strength += 32;
						break;
					case 4:
					default:
						if (fieldSide == playerSide)
							return WIN_VALUE;
						else
							lose = true;
					}


					//Reset the counter to 1
					tokensInARowCounter = 1;
					//Check above the current location
					for (y = j-1; y >= 0; y--)
					{
						if (fieldSide == playingField[i][y])
						{
							tokensInARowCounter++;
						}
						else
						{
							break;
						}
					}
					//Vertical - check
					switch (tokensInARowCounter)
					{
					case 1:
						strength++;
						break;
					case 2:
						strength += 4;
						break;
					case 3:
						strength += 32;
						break;
					case 4:
					default:
						if (fieldSide == playerSide)
							return WIN_VALUE;
						else
							lose = true;
					}



					//Reset the counter to 1
					tokensInARowCounter = 1;
					//Diagonal - up left
					for (x = i-1, y = j-1; x >= 0 && y >= 0; x--, y--)
					{
						if (fieldSide == playingField[x][y])
						{
							tokensInARowCounter++;
						}
						else
						{
							break;
						}
					}
					//Diagonal - check
					switch (tokensInARowCounter)
					{
					case 1:
						strength++;
						break;
					case 2:
						strength += 4;
						break;
					case 3:
						strength += 32;
						break;
					case 4:
					default:
						if (fieldSide == playerSide)
							return WIN_VALUE;
						else
							lose = true;
					}
					
					//If the current side is HUMAN then the value is negative instead of positive
					if (fieldSide == playerSide)
					{
						totalstrength += strength;
					}
					else
					{
						totalstrength -= strength;
					}
					
				}
			}
		}
		
		//If the opponent has a four on a row
		if (lose) {
			return LOSE_VALUE;
		}
		
		assert(totalstrength > LOSE_VALUE);
		assert(totalstrength < WIN_VALUE);
		
		return totalstrength;
	}
	
	protected boolean isDraw()
	{
		for (int i = 0; i < Game.XSIZE; i++)
		{
			if (! columnIsFull(i))
			{
				//Geen gelijk spel
				return false;
			}
		}
		//Gelijk spel
		return true;
	}

}
